package com.guosen.zebra.console.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class PBResolver {
	private static final Logger logger = LogManager.getLogger(PBResolver.class);	
	
	public static JSONObject getPbResolver(String proto) {
		proto = proto.replaceAll("}syntax", "}\r\nsyntax");
		List<String> protos = Arrays.asList(proto.split("syntax"));
		JSONArray msgs = new JSONArray();
		JSONArray mds = new JSONArray();
		protos.forEach((pb) -> {
			Pattern r = Pattern.compile("package\\s+([a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*);");
			Matcher m = r.matcher(pb);
			String pbPkg = "";
			if (m.find()) {
				pbPkg = m.group(1);
			}

			pb = pb.replaceAll("[syntax]*[\\d\\D]+\"proto3\";", "");
			pb = pb.replaceAll(
					"option\\s+java_package\\s*=\\s*\"([a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*)\";\r\n", "");
			pb = pb.replaceAll("option\\s+java_outer_classname\\s*=\\s*\"\\w+\";\r\n", "");
			pb = pb.replaceAll("package\\s+([a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*);\r\n", "");
			pb = pb.replaceAll("\\s*import\\s+\"\\S+\"\\s*;\\s*\r\n", "");
			pb = pb.replaceAll("(//)*\\s*option\\s+java_multiple_files\\s*=\\s*true\\s*;", "");
			JSONArray array = getPbMessage(pb, pbPkg);
			if (array != null) {
				msgs.addAll(array);
			}

			array = getPbService(pb);
			if (array != null) {
				mds.addAll(array);
			}

		});
		JSONObject result = new JSONObject();
		result.put("msgs", msgs);
		result.put("mds", mds);
		return result;
	}

	public static JSONArray getPbMessage(String pb, String pkgName) {
		Pattern r = Pattern.compile("([//]*.*[\r\n]*\\s*message\\s+\\w+\\s*\\{[^[{[\\d\\D]}]]*\\})");
		Matcher m = r.matcher(pb);
		if (m.find()) {
			String msg = m.group();
			Map<String, Object> map = getMessages(msg);
			List<JSONObject> List = tranferMap2List(pkgName, map);
			JSONArray array = new JSONArray();
			array.addAll(List);
			return array;
		} else {
			return null;
		}
	}

	public static JSONArray getPbService(String pb) {
		Pattern r = Pattern.compile("([//]*.*[\r\n]*\\s*service\\s+\\w+\\s*\\{[^[{[\\d\\D]}]]*\\})");
		Matcher m = r.matcher(pb);
		if (m.find()) {
			String msg = m.group();
			return getServiceDefine(msg);
		} else {
			return null;
		}
	}

	private static Map<String, Object> getMessages(String msg) {
		StringBuffer ctx = new StringBuffer();
		int i = 0;
		Map<String, Object> inMap = Maps.newHashMap();
		int inNum = 0;

		while (i < msg.length() && msg.charAt(i) != 0) {
			String context;
			String keyName;
			if ("}".charAt(0) == msg.charAt(i)) {
				inMap.put("idx", i + 1);
				context = ctx.toString().trim();
				keyName = "message";
				Pattern r = Pattern.compile("([//]+.*([\r\n]+.*[\r\n]+))*\\s*message\\s+(\\w+)");
				Matcher m = r.matcher(context);
				if (context.contains("oneof")) {
					keyName = "oneof";
				} else if (!m.find()) {
					keyName = "field";
				} else {
					context = context.replaceAll("//.*[\r\n]+", "");
					context = context.substring(context.indexOf("message") + 7).trim();
				}

				if (inMap.get("message" + inNum) == null && !StringUtils.isEmpty(context)) {
					inMap.put(keyName + inNum, context);
				}

				return inMap;
			}

			if ("{".charAt(0) != msg.charAt(i) && ";".charAt(0) != msg.charAt(i)) {
				ctx.append(msg.charAt(i));
			} else {
				if ("{".charAt(0) == msg.charAt(i)) {
					context = ctx.toString().trim();
					keyName = "message";
					Pattern r = Pattern.compile("([//]+.*([\r\n]+.*[\r\n]+))*\\s*message\\s+(\\w+)");
					Matcher m = r.matcher(context);
					if (context.contains("oneof")) {
						keyName = "oneof";
					} else if (!m.find()) {
						keyName = "field";
					} else {
						context = context.replaceAll("//.*[\r\n]+", "");
						context = context.substring(context.indexOf("message") + 7).trim();
					}

					inMap.put(keyName + inNum, context);
					ctx = new StringBuffer();
					++i;
					Map<String, Object> in = getMessages(msg.substring(i, msg.length()));
					inMap.put("in" + inNum, in);
					++inNum;
					i += (Integer) in.get("idx");
					continue;
				}

				if (";".charAt(0) == msg.charAt(i)) {
					context = ctx.toString().trim();
					if (context != null && !"".equals(context)) {
						inMap.put("field" + inNum, context);
						ctx = new StringBuffer();
						++inNum;
					}
				}
			}

			++i;
		}

		return inMap;
	}
	@SuppressWarnings("unchecked")
	private static List<JSONObject> tranferMap2List(String packgeName, Map<String, Object> param) {
		List<JSONObject> list = Lists.newArrayList();

		for (int i = 0; param.get("message" + i) != null; ++i) {
			JSONObject map = new JSONObject();
			String msgName = (String) param.get("message" + i);
			map.put("name", packgeName + "." + msgName);
			
			Map<String, Object> body = (Map<String, Object>) param.get("in" + i);
			int j = 0;

			for (int idx = 0; body.get("field" + j) != null || body.get("oneof" + j) != null; ++j) {
				String field = (String) body.get("field" + j);
				Map<String,String> fieldMap = Maps.newConcurrentMap();
				if (field == null) {
					field = (String) body.get("oneof" + j);
					field = field.substring(0, field.indexOf("oneof"));
					fieldMap =  (Map<String, String>) body.get("in" + j);
					field = field + " oneof " + fieldMap.get("field0");
				}

				if (field.indexOf("=") > 0) {
					field = field.substring(0, field.indexOf("="));
				}

				field = field.replaceAll(";", "");
				field = field.trim();
				fieldMap = getField(packgeName, field);
				if (!fieldMap.isEmpty()) {
					map.put("field" + idx, fieldMap);
					++idx;
				}
			}

			list.add(map);
		}

		return list;
	}

	private static Map<String, String> getField(String packgeName, String field) {
		Map<String, String> map = Maps.newHashMap();
		Pattern r = Pattern.compile("[//]+\\s*.*[\u4e00-\u9fa5]*.*[\r\n]+");
		Matcher m = r.matcher(field);

		while (m.find()) {
			map.put("commont", m.group().replaceAll("//", ""));
		}

		r = Pattern.compile("(\\w+\\s+)*(\\w+)\\s+(\\w+)\\s*");
		m = r.matcher(field);
		ArrayList<String> list = Lists.newArrayList();

		while (m.find()) {
			list.add(m.group(1));
			list.add(m.group(2));
			list.add(m.group(3));
		}

		if (list.size() == 3) {
			map.put("option", list.get(0) == null ? "" : ((String) list.get(0)).trim().replaceAll("\r\n", ""));
			map.put("type", ((String) list.get(1)).trim().replaceAll("\r\n", ""));
			map.put("name", ((String) list.get(2)).trim().replaceAll("\r\n", ""));
		} else {
			r = Pattern.compile("(\\w+\\s+)*([a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*)\\s+(\\w+)\\s*");
			m = r.matcher(field);

			while (m.find()) {
				map.put("option", m.group(1));
				map.put("type", m.group(2));
				map.put("name", m.group(4));
			}
		}

		checkRef(packgeName, map);
		return map;
	}

	private static void checkRef(String packgeName, Map<String, String> map) {
		try{
			if (!map.isEmpty()) {
				List<String> list = Arrays
						.asList("bool,double,float,int32,uin32,int64,uint64,sint32,sing64,fixed32,fixed64,sfixed32,sfixed64,string,bytes,enum,message"
								.split(","));
				if (!list.contains(map.get("type"))) {
					if (map.get("type") != null && !((String) map.get("type")).contains("map")) {
						if (((String) map.get("type")).split("\\.").length > 1) {
							map.put("ref", map.get("type"));
						} else {
							map.put("ref", packgeName + "." + (String) map.get("type"));
						}

					}
				}
			}
		}catch(Exception e){
			logger.error(e.getMessage(), e);
		}
		
	}

	public static JSONArray getServiceDefine(String msg) {
		JSONArray array = new JSONArray();
		msg = msg.replaceAll("//\\s*(###(.*)###)","");
		msg = msg.replaceAll("[\r\n]*\\s*service\\s+\\w+\\s*\\{", "");
		msg = msg.substring(0, msg.lastIndexOf("}") - 1);
		String[] mds = msg.split("}");

		for (String md : mds) {
			md = md.replace("{", "");
			md = md.trim();
			JSONObject json = new JSONObject();
			Pattern r = Pattern.compile("([//]+.*([\r\n]+.*[\r\n]+))+");

			Matcher m = r.matcher(md);
			while (m.find()) {
				json.put("commont", m.group().replaceAll("//", ""));
			}

			md = md.trim();
			r = Pattern.compile(
					"rpc\\s+(\\w+)\\s*\\(((\\s*[a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*\\s*))\\)\\s*returns\\s*\\(((\\s*[a-zA-Z]+[0-9a-zA-Z_]*(\\.[a-zA-Z]+[0-9a-zA-Z_]*)*\\s*))\\)");
			m = r.matcher(md);

			while (m.find()) {
				json.put("name", m.group(1));
				json.put("req", m.group(2));
				json.put("rsp", m.group(5));
			}

			array.add(json);
		}

		return array;
	}

	public static String getServiceDesc(String pb){
		Pattern r = Pattern.compile("(###(.*)###)");
		Matcher m = r.matcher(pb);
		while (m.find()) {
			return m.group(2);
		}
		return null;
	}
	public static void main(String[] args) {
//		String a  = "\noption java_package = \"com.guosen.tradeasset.model.asset\";\noption java_outer_classname = \"IsFundidHoldStocksModel\";\npackage com.guosen.tradeasset.model;\nimport \"zebra/common_dto.proto\";\n\n/**************************************************************************\n\n# 判断客户T-1日是否持仓某证券\n\n## 序列图\n\n```seq \n\n\nluaserv->>trade_mid: 410.8-判断客户T-1日是否持仓某证券\n\ntrade_mid->>trade_mid: lua:///isFundidHoldStocks\n\ntrade_mid->>trade_mid: 判断最多只传5只股票\n\ntrade_mid->>IA: 481.1-查看客户T-1日是否持仓某证券\nIA->>DB: ifc_caas.dbo.ifc_dd_stockhold\n\ntrade_mid->>trade_mid: 拼接客户的持仓列表\n\n\n``` \n\n **************************************************************************/\n\n// 请求入参\nmessage IsFundidHoldStocksRequest {\n    // 交易认证串\n    string randomKey = 1;\n\n    // 资金账号\n    string fundid = 2;\n\n    // 使用,分隔股票代码, 示例: xxxxxx.x,xxxxxx.x\n    string stocks = 3;\n\n}\n\n// 响应出参\nmessage IsFundidHoldStocksDTO {\n    // 股票.市场，使用行情市场, xxxxxx.x\n    string stock = 1;\n\n    // 是否持仓\n    string isHold = 2;\n}\n\nmessage IsFundidHoldStocksReply {\n    repeated com.guosen.zebra.dto.ResultDTO result = 1;\n    repeated IsFundidHoldStocksDTO data = 2;\n}\n\n";
//		getPbMessage(a, "com.guosen.tradeasset.model");
		String field = "// 交易认证串 \r\n";
		Pattern r = Pattern.compile("[//]+\\s*.*[\u4e00-\u9fa5]*.*[\r\n]+");
		Matcher m = r.matcher(field);

		while (m.find()) {
			System.err.println(m.group());
		}
	}
//	
//	
//	public static String readFile() {
//		String pathname = "C:\\Users\\guosen\\Desktop\\WeChatPayService.proto"; // 绝对路径或相对路径都可以，写入文件时演示相对路径,读取以上路径的input.txt文件
//		// 防止文件建立或读取失败，用catch捕捉错误并打印，也可以throw;
//		// 不关闭文件会导致资源的泄露，读写文件都同理
//		// Java7的try-with-resources可以优雅关闭文件，异常时自动关闭文件；详细解读https://stackoverflow.com/a/12665271
//		try (FileReader reader = new FileReader(pathname); BufferedReader br = new BufferedReader(reader) // 建立一个对象，它把文件内容转成计算机能读懂的语言
//		) {
//			StringBuffer text = new StringBuffer();
//			String line;
//			// 网友推荐更加简洁的写法
//			while ((line = br.readLine()) != null) {
//				// 一次读入一行数据
//				text.append(line).append("\r\n");
//			}
//			return text.toString();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		return "";
//	}

}